package com.h2cloud.Proxy._4_cglibProxy;

import lombok.Data;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @Package: com.h2cloud.Proxy._4_cglibProxy
 * @ClassName: Client
 * @Author: h2cloud
 * @CreateTime: 2022/8/18 12:37 上午
 * @Description:
 */

// 一个生产者
@Data
class Producer {
	String name = "一个生产者";

	// 销售
	public void saleProduct(float money) {
		System.out.println("销售产品，并拿到钱：" + money);
	}

	// 售后
	public void afterService(float money) {
		System.out.println("this: " + this);
		this.saleProduct(money);
		System.out.println("提供售后服务，并拿到钱：" + money);
	}
}

public class Client {
	public static void main(String[] args) throws NoSuchMethodException {
		final Producer producer = new Producer();
		/**
		 *  基于子类的动态代理：
		 *      涉及的类：Enhancer
		 *      提供者：第三方 cglib库
		 *  如何创建代理对象：
		 *      使用 Enhancer类中的 create方法
		 *  创建代理对象的要求：
		 *      被代理类不能是最终类
		 *  create方法的参数：
		 *      Class：字节码
		 *          它是用于指定被代理对象的字节码。
		 *      Callback：用于提供增强的代码
		 *          一般写的都是该接口的子接口实现类：MethodInterceptor
		 */
		Producer cglibProducer = (Producer) Enhancer.create(
				producer.getClass(),
				new MethodInterceptor() {
					/**
					 * 执行被代理对象的任何方法都会经过该方法
					 * @param proxy
					 * @param method
					 * @param args
					 *    以上三个参数和基于接口的动态代理中 invoke方法的参数是一样的
					 * @param methodProxy ：当前执行方法的代理对象
					 * @return
					 * @throws Throwable
					 */
					@Override
					public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
						//2.判断当前方法是不是销售
						if ("saleProduct".equals(method.getName())) {
							//1.获取方法执行的参数
							Float money = (Float) args[0];
							return method.invoke(producer, money * 0.8f);
						}
						return method.invoke(producer);
					}
				});
		cglibProducer.saleProduct(12000f);

		System.out.println("cglibProducer.getName() = " + cglibProducer.getName());

		for (Method declaredMethod : Producer.class.getDeclaredMethods()) {
			System.out.println("declaredMethod.getName() = " + declaredMethod.getName());
			boolean aPublic = Modifier.isPublic(declaredMethod.getModifiers());
			System.out.println("aPublic = " + aPublic);
		}
	}
}
